import { EventEmitter } from '../utils/EventEmitter';
import {
    Resource,
    ResourceType,
    ResourceState,
    ResourceLoadOptions,
    ResourceManagerOptions,
    ResourceEventType,
    ResourceEventData,
    ResourceEventHandler,
    ImageResource,
    AudioResource,
    JsonResource,
    TextResource,
    FontResource
} from './types';

/**
 * 资源管理器类
 * 负责加载和管理游戏资源
 */
export class ResourceManager extends EventEmitter {
    private resources: Map<string, Resource>;
    private loadingQueue: ResourceLoadOptions[];
    private loadingCount: number;
    private options: Required<ResourceManagerOptions>;
    
    constructor(options: ResourceManagerOptions = {}) {
        super();
        
        // 初始化资源管理器选项
        this.options = {
            timeout: 30000,
            maxConcurrent: 4,
            enableCache: true,
            cacheExpiration: 3600000, // 1小时
            retryCount: 3,
            retryDelay: 1000,
            priorityQueueSize: 100,
            throwOnError: false,
            ...options
        };
        
        // 初始化资源存储
        this.resources = new Map();
        this.loadingQueue = [];
        this.loadingCount = 0;
    }
    
    /**
     * 加载单个资源
     */
    public async load(options: ResourceLoadOptions): Promise<Resource> {
        // 检查资源是否已加载
        const name = options.name || this.getResourceNameFromUrl(options.url);
        const cached = this.resources.get(name);
        
        if (cached && this.options.enableCache) {
            if (Date.now() - cached.loadTime < this.options.cacheExpiration) {
                this.emit(ResourceEventType.CACHE_HIT, {
                    name: cached.name,
                    type: cached.type,
                    url: cached.url,
                    loadTime: cached.loadTime
                });
                return cached;
            }
        }
        
        this.emit(ResourceEventType.CACHE_MISS, {
            name,
            type: options.type,
            url: options.url
        });
        
        // 创建资源对象
        const resource = this.createResource(name, options);
        this.resources.set(name, resource);
        
        // 添加到加载队列
        this.loadingQueue.push(options);
        this.processLoadingQueue();
        
        // 等待资源加载完成
        return new Promise((resolve, reject) => {
            const timeout = setTimeout(() => {
                const error = new Error(`Resource load timeout: ${options.url}`);
                this.handleLoadError(resource, error);
                if (this.options.throwOnError) {
                    reject(error);
                }
            }, options.timeout || this.options.timeout);
            
            const onLoad = (event: ResourceEventData) => {
                if (event.name === name) {
                    clearTimeout(timeout);
                    this.removeListener(ResourceEventType.LOAD_COMPLETE, onLoad);
                    this.removeListener(ResourceEventType.LOAD_ERROR, onError);
                    resolve(resource);
                }
            };
            
            const onError = (event: ResourceEventData) => {
                if (event.name === name) {
                    clearTimeout(timeout);
                    this.removeListener(ResourceEventType.LOAD_COMPLETE, onLoad);
                    this.removeListener(ResourceEventType.LOAD_ERROR, onError);
                    if (this.options.throwOnError) {
                        reject(event.error);
                    }
                }
            };
            
            this.on(ResourceEventType.LOAD_COMPLETE, onLoad);
            this.on(ResourceEventType.LOAD_ERROR, onError);
        });
    }
    
    /**
     * 批量加载资源
     */
    public async loadAll(options: ResourceLoadOptions[]): Promise<Resource[]> {
        return Promise.all(options.map(opt => this.load(opt)));
    }
    
    /**
     * 获取已加载的资源
     */
    public get(name: string): Resource | undefined {
        return this.resources.get(name);
    }
    
    /**
     * 检查资源是否已加载
     */
    public isLoaded(name: string): boolean {
        const resource = this.resources.get(name);
        return resource?.state === ResourceState.LOADED;
    }
    
    /**
     * 卸载资源
     */
    public unload(name: string): void {
        const resource = this.resources.get(name);
        if (resource) {
            // 释放资源
            if (resource.type === ResourceType.IMAGE) {
                (resource as ImageResource).data.src = '';
            } else if (resource.type === ResourceType.AUDIO) {
                (resource as AudioResource).data.src = '';
            }
            
            // 从缓存中移除
            this.resources.delete(name);
        }
    }
    
    /**
     * 卸载所有资源
     */
    public unloadAll(): void {
        for (const [name] of this.resources) {
            this.unload(name);
        }
    }
    
    /**
     * 获取资源加载进度
     */
    public getProgress(): number {
        if (this.resources.size === 0) return 1;
        
        let totalProgress = 0;
        for (const resource of this.resources.values()) {
            totalProgress += resource.progress;
        }
        
        return totalProgress / this.resources.size;
    }
    
    /**
     * 处理加载队列
     */
    private processLoadingQueue(): void {
        while (this.loadingCount < this.options.maxConcurrent && this.loadingQueue.length > 0) {
            const options = this.loadingQueue.shift()!;
            this.loadResource(options);
        }
    }
    
    /**
     * 加载资源
     */
    private async loadResource(options: ResourceLoadOptions): Promise<void> {
        const name = options.name || this.getResourceNameFromUrl(options.url);
        const resource = this.resources.get(name)!;
        
        try {
            this.loadingCount++;
            resource.state = ResourceState.LOADING;
            
            this.emit(ResourceEventType.LOAD_START, {
                name: resource.name,
                type: resource.type,
                url: resource.url
            });
            
            switch (options.type) {
                case ResourceType.IMAGE:
                    await this.loadImage(resource as ImageResource);
                    break;
                case ResourceType.AUDIO:
                    await this.loadAudio(resource as AudioResource);
                    break;
                case ResourceType.JSON:
                    await this.loadJson(resource as JsonResource);
                    break;
                case ResourceType.TEXT:
                    await this.loadText(resource as TextResource);
                    break;
                case ResourceType.FONT:
                    await this.loadFont(resource as FontResource);
                    break;
            }
            
            resource.state = ResourceState.LOADED;
            resource.progress = 1;
            resource.loadTime = Date.now();
            
            this.emit(ResourceEventType.LOAD_COMPLETE, {
                name: resource.name,
                type: resource.type,
                url: resource.url,
                loadTime: resource.loadTime
            });
        } catch (error) {
            this.handleLoadError(resource, error as Error);
        } finally {
            this.loadingCount--;
            this.processLoadingQueue();
        }
    }
    
    /**
     * 加载图片资源
     */
    private loadImage(resource: ImageResource): Promise<void> {
        return new Promise((resolve, reject) => {
            const img = new Image();
            
            img.onload = () => {
                resource.data = img;
                resource.width = img.width;
                resource.height = img.height;
                resolve();
            };
            
            img.onerror = () => {
                reject(new Error(`Failed to load image: ${resource.url}`));
            };
            
            img.src = resource.url;
        });
    }
    
    /**
     * 加载音频资源
     */
    private loadAudio(resource: AudioResource): Promise<void> {
        return new Promise((resolve, reject) => {
            const audio = new Audio();
            
            audio.oncanplaythrough = () => {
                resource.data = audio;
                resource.duration = audio.duration;
                resolve();
            };
            
            audio.onerror = () => {
                reject(new Error(`Failed to load audio: ${resource.url}`));
            };
            
            audio.src = resource.url;
            audio.load();
        });
    }
    
    /**
     * 加载JSON资源
     */
    private async loadJson(resource: JsonResource): Promise<void> {
        try {
            const response = await fetch(resource.url);
            if (!response.ok) {
                throw new Error(`HTTP error! status: ${response.status}`);
            }
            resource.data = await response.json();
        } catch (error) {
            throw new Error(`Failed to load JSON: ${resource.url}`);
        }
    }
    
    /**
     * 加载文本资源
     */
    private async loadText(resource: TextResource): Promise<void> {
        try {
            const response = await fetch(resource.url);
            if (!response.ok) {
                throw new Error(`HTTP error! status: ${response.status}`);
            }
            resource.data = await response.text();
        } catch (error) {
            throw new Error(`Failed to load text: ${resource.url}`);
        }
    }
    
    /**
     * 加载字体资源
     */
    private async loadFont(resource: FontResource): Promise<void> {
        try {
            const font = new FontFace(resource.name, `url(${resource.url})`);
            const loadedFont = await font.load();
            document.fonts.add(loadedFont);
            resource.data = loadedFont;
        } catch (error) {
            throw new Error(`Failed to load font: ${resource.url}`);
        }
    }
    
    /**
     * 处理加载错误
     */
    private handleLoadError(resource: Resource, error: Error): void {
        resource.state = ResourceState.ERROR;
        resource.error = error;
        resource.progress = 0;
        
        this.emit(ResourceEventType.LOAD_ERROR, {
            name: resource.name,
            type: resource.type,
            url: resource.url,
            error
        });
        
        // 尝试重试
        const currentRetryCount = resource.retryCount || 0;
        if (currentRetryCount < this.options.retryCount) {
            resource.retryCount = currentRetryCount + 1;
            setTimeout(() => {
                this.loadingQueue.push({
                    type: resource.type,
                    url: resource.url,
                    name: resource.name,
                    retryCount: resource.retryCount
                });
                this.processLoadingQueue();
            }, this.options.retryDelay);
        }
    }
    
    /**
     * 创建资源对象
     */
    private createResource(name: string, options: ResourceLoadOptions): Resource {
        const baseResource: Resource = {
            name,
            type: options.type,
            url: options.url,
            state: ResourceState.PENDING,
            data: null,
            progress: 0,
            loadTime: 0,
            retryCount: 0
        };
        
        switch (options.type) {
            case ResourceType.IMAGE:
                return { ...baseResource, width: 0, height: 0 } as ImageResource;
            case ResourceType.AUDIO:
                return { ...baseResource, duration: 0 } as AudioResource;
            case ResourceType.JSON:
                return { ...baseResource, data: null } as JsonResource;
            case ResourceType.TEXT:
                return { ...baseResource, data: '' } as TextResource;
            case ResourceType.FONT:
                // 使用类型断言，因为 FontFace 需要异步加载
                return { ...baseResource, data: null as unknown as FontFace } as FontResource;
            default:
                return baseResource;
        }
    }
    
    /**
     * 从URL获取资源名称
     */
    private getResourceNameFromUrl(url: string): string {
        const parts = url.split('/');
        const lastPart = parts[parts.length - 1];
        return lastPart.split('.')[0];
    }
    
    /**
     * 销毁资源管理器
     */
    public destroy(): void {
        this.unloadAll();
        this.loadingQueue = [];
        this.loadingCount = 0;
        this.removeAllListeners();
    }
} 